use crate::common::data_bind::{WrappedPath, WrappedQuery, WrappedRequestBody};
use crate::error::ApiError;
use axum::extract::{FromRequest, FromRequestParts, Request};
use axum::http::request::Parts;
use axum_valid::Valid;

#[derive(Debug, Clone, Default, FromRequest, FromRequestParts)]
#[from_request(via(Valid), rejection(ApiError))]
pub struct WrappedValid<T>(pub T);


#[derive(Debug, Clone, Default)]
pub struct WrappedValidQuery<T>(pub T);
#[derive(Debug, Clone, Default)]
pub struct WrappedValidPath<T>(pub T);
#[derive(Debug, Clone, Default)]
pub struct WrappedValidRequestBody<T>(pub T);

macro_rules! impl_from_request {
    ($name:ident,$wrapper:ident,FromRequestParts) => {
        impl<S, T> FromRequestParts<S> for $name<T>
        where
            S: Send + Sync,
            WrappedValid<$wrapper<T>>: FromRequestParts<S, Rejection=ApiError>,
        {
            type Rejection = ApiError;

            async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
                Ok($name(WrappedValid::from_request_parts(parts, state).await?.0.0))
            }
        }
    };

    ($name:ident,$wrapper:ident,FromRequest) => {
        impl<S, T> FromRequest<S> for $name<T>
        where
            S: Send + Sync,
            WrappedValid<$wrapper<T>>: FromRequest<S, Rejection=ApiError>,
        {
            type Rejection = ApiError;

             async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
                Ok($name(WrappedValid::from_request(req, state).await?.0.0))
            }
        }
    };
}

impl_from_request!(WrappedValidRequestBody,WrappedRequestBody,FromRequest);
impl_from_request!(WrappedValidQuery,WrappedQuery,FromRequestParts);
impl_from_request!(WrappedValidPath,WrappedPath,FromRequestParts);
